`include "defines.v"
`define AXI_TOP_INTERFACE(name) io_memAXI_0_``name

module SimTop(
    input  wire         clock,
    input  wire         reset,

    input  wire [63: 0] io_logCtrl_log_begin,
    input  wire [63: 0] io_logCtrl_log_end,
    input  wire [63: 0] io_logCtrl_log_level,
    input  wire         io_perfInfo_clean,
    input  wire         io_perfInfo_dump,

    output wire         io_uart_out_valid,
    output wire [ 7: 0] io_uart_out_ch,
    output wire         io_uart_in_valid,
    input  wire [ 7: 0] io_uart_in_ch,

    input                               `AXI_TOP_INTERFACE(aw_ready),
    output                              `AXI_TOP_INTERFACE(aw_valid),
    output [`AXI_ADDR_WIDTH-1:0]        `AXI_TOP_INTERFACE(aw_bits_addr),
    output [2:0]                        `AXI_TOP_INTERFACE(aw_bits_prot),
    output [`AXI_ID_WIDTH+1:0]          `AXI_TOP_INTERFACE(aw_bits_id),
    output [`AXI_USER_WIDTH-1:0]        `AXI_TOP_INTERFACE(aw_bits_user),
    output [7:0]                        `AXI_TOP_INTERFACE(aw_bits_len),
    output [2:0]                        `AXI_TOP_INTERFACE(aw_bits_size),
    output [1:0]                        `AXI_TOP_INTERFACE(aw_bits_burst),
    output                              `AXI_TOP_INTERFACE(aw_bits_lock),
    output [3:0]                        `AXI_TOP_INTERFACE(aw_bits_cache),
    output [3:0]                        `AXI_TOP_INTERFACE(aw_bits_qos),
    
    input                               `AXI_TOP_INTERFACE(w_ready),
    output                              `AXI_TOP_INTERFACE(w_valid),
    output [`AXI_DATA_WIDTH-1:0]        `AXI_TOP_INTERFACE(w_bits_data)         [3:0],
    output [`AXI_DATA_WIDTH/8-1:0]      `AXI_TOP_INTERFACE(w_bits_strb),
    output                              `AXI_TOP_INTERFACE(w_bits_last),
    
    output                              `AXI_TOP_INTERFACE(b_ready),
    input                               `AXI_TOP_INTERFACE(b_valid),
    input  [1:0]                        `AXI_TOP_INTERFACE(b_bits_resp),
    input  [`AXI_ID_WIDTH+1:0]          `AXI_TOP_INTERFACE(b_bits_id),
    input  [`AXI_USER_WIDTH-1:0]        `AXI_TOP_INTERFACE(b_bits_user),

    input                               `AXI_TOP_INTERFACE(ar_ready),
    output                              `AXI_TOP_INTERFACE(ar_valid),
    output [`AXI_ADDR_WIDTH-1:0]        `AXI_TOP_INTERFACE(ar_bits_addr),
    output [2:0]                        `AXI_TOP_INTERFACE(ar_bits_prot),
    output [`AXI_ID_WIDTH+1:0]          `AXI_TOP_INTERFACE(ar_bits_id),
    output [`AXI_USER_WIDTH-1:0]        `AXI_TOP_INTERFACE(ar_bits_user),
    output [7:0]                        `AXI_TOP_INTERFACE(ar_bits_len),
    output [2:0]                        `AXI_TOP_INTERFACE(ar_bits_size),
    output [1:0]                        `AXI_TOP_INTERFACE(ar_bits_burst),
    output                              `AXI_TOP_INTERFACE(ar_bits_lock),
    output [3:0]                        `AXI_TOP_INTERFACE(ar_bits_cache),
    output [3:0]                        `AXI_TOP_INTERFACE(ar_bits_qos),
    
    output                              `AXI_TOP_INTERFACE(r_ready),
    input                               `AXI_TOP_INTERFACE(r_valid),
    input  [1:0]                        `AXI_TOP_INTERFACE(r_bits_resp),
    input  [`AXI_DATA_WIDTH-1:0]        `AXI_TOP_INTERFACE(r_bits_data)         [3:0],
    input                               `AXI_TOP_INTERFACE(r_bits_last),
    input  [`AXI_ID_WIDTH+1:0]          `AXI_TOP_INTERFACE(r_bits_id),
    input  [`AXI_USER_WIDTH-1:0]        `AXI_TOP_INTERFACE(r_bits_user)
);

//CPU TOPMAIN

//for sim
wire rd_w_ena;
wire [4:0] rd_w_addr;
wire [`XLEN-1:0] rd_data;
wire [63: 0] pc;
wire [`INSTLEN-1:0] inst;
wire [3:0] stall;
wire flush;
wire mem_okay;
wire inst_valid;
wire clint_skip;
wire bubble;
wire [`MXLEN-1:0] mhartid;
wire [`MXLEN-1:0] mstatus;
wire [`MXLEN-1:0] mie;
wire [`MXLEN-1:0] mtvec;
wire [`MXLEN-1:0] mscratch;
wire [`MXLEN-1:0] mepc;
wire [`MXLEN-1:0] mcause;
wire [`MXLEN-1:0] mip;
wire [`MXLEN-1:0] mcycle;
wire [`MXLEN-1:0] minstret;
wire timer_trap;

RVCPU RVCPU (
    .clock(clock),
    .reset(reset),
    .stall(stall),
    .flush(flush),

    // Advanced eXtensible Interface
    .axi_aw_ready               (aw_ready),
    .axi_aw_valid               (aw_valid),
    .axi_aw_addr                (aw_addr),
    .axi_aw_prot                (aw_prot),
    .axi_aw_id                  (aw_id),
    .axi_aw_user                (aw_user),
    .axi_aw_len                 (aw_len),
    .axi_aw_size                (aw_size),
    .axi_aw_burst               (aw_burst),
    .axi_aw_lock                (aw_lock),
    .axi_aw_cache               (aw_cache),
    .axi_aw_qos                 (aw_qos),
    .axi_aw_region              (aw_region),

    .axi_w_ready                (w_ready),
    .axi_w_valid                (w_valid),
    .axi_w_data                 (w_data),
    .axi_w_strb                 (w_strb),
    .axi_w_last                 (w_last),
    .axi_w_user                 (w_user),

    .axi_b_ready                (b_ready),
    .axi_b_valid                (b_valid),
    .axi_b_resp                 (b_resp),
    .axi_b_id                   (b_id),
    .axi_b_user                 (b_user),

    .axi_ar_ready               (ar_ready),
    .axi_ar_valid               (ar_valid),
    .axi_ar_addr                (ar_addr),
    .axi_ar_prot                (ar_prot),
    .axi_ar_id                  (ar_id),
    .axi_ar_user                (ar_user),
    .axi_ar_len                 (ar_len),
    .axi_ar_size                (ar_size),
    .axi_ar_burst               (ar_burst),
    .axi_ar_lock                (ar_lock),
    .axi_ar_cache               (ar_cache),
    .axi_ar_qos                 (ar_qos),
    .axi_ar_region              (ar_region),

    .axi_r_ready                (r_ready),
    .axi_r_valid                (r_valid),
    .axi_r_resp                 (r_resp),
    .axi_r_data                 (r_data),
    .axi_r_last                 (r_last),
    .axi_r_id                   (r_id),
    .axi_r_user                 (r_user),

    //for sim
    .trap(timer_trap),
    .gpr(regs_diff),
    .RegWr(rd_w_ena),
    .rd_addr(rd_w_addr),
    .data_to_reg(rd_data),
    .pc_addr(pc),
    .inst(inst),
    .mem_okay(mem_okay),
    .inst_valid(inst_valid),
    .clint_skip(clint_skip),
    .bubble(bubble),

    .mhartid(mhartid),
    .mstatus(mstatus),
    .mie(mie),
    .mtvec(mtvec),
    .mscratch(mscratch),
    .mepc(mepc),
    .mcause(mcause),
    .mip(mip),
    .mcycle(mcycle),
    .minstret(minstret)
);


    // Difftest
    reg cmt_wen;
    reg [7:0] cmt_wdest;
    reg [`XLEN-1:0] cmt_wdata;
    reg [`XLEN-1:0] cmt_pc_delay1;
    reg [`XLEN-1:0] cmt_pc_delay2;
    reg [`XLEN-1:0] cmt_pc_delay3;
    reg [`XLEN-1:0] cmt_pc;
    reg [31:0] cmt_inst_delay1;
    reg [31:0] cmt_inst_delay2;
    reg [31:0] cmt_inst_delay3;
    reg [31:0] cmt_inst;
    reg cmt_valid;
    wire trap;
    //reg [7:0] trap_code;
    reg [63:0] cycleCnt;
    reg [63:0] instrCnt;
    reg [`XLEN-1:0] regs_diff [31: 0];
    wire        valid;

    assign valid = inst_okay[3] & ~stall[3];
    assign trap = (cmt_inst[6:0] == 7'h6b);

    always@(posedge clock) begin
      if (cmt_inst[6:0] == 7'h7b & cmt_valid) begin
        $write("%c",regs_diff[10]);
      end
    end

    always @(posedge clock) begin
      if (reset) begin
        {cmt_wen, cmt_wdest, cmt_wdata, cmt_pc_delay1 , cmt_pc_delay2 , cmt_pc_delay3 , cmt_pc, cmt_inst, cmt_inst_delay1 , cmt_inst_delay2 , cmt_inst_delay3 , cmt_valid, cycleCnt, instrCnt} <= 0;
      end
      else if (~trap) begin
        cmt_wen <= rd_w_ena;
        cmt_wdest <= {3'd0, rd_w_addr};
        cmt_wdata <= (|rd_w_addr) ? rd_data : 64'b0 ;

        cmt_pc_delay1 <= stall[1] ? cmt_pc_delay1 : pc;
        cmt_pc_delay2 <= stall[2] ? cmt_pc_delay2 : cmt_pc_delay1;
        cmt_pc_delay3 <= stall[3] ? cmt_pc_delay3 : cmt_pc_delay2;

        if(valid) begin
          cmt_pc <= cmt_pc_delay3;
        end
        else begin
          cmt_pc <= cmt_pc;
        end

        cmt_inst_delay1 <= stall[1] ? cmt_inst_delay1 : inst;
        cmt_inst_delay2 <= stall[2] ? cmt_inst_delay2 : cmt_inst_delay1;
        cmt_inst_delay3 <= stall[3] ? cmt_inst_delay3 : cmt_inst_delay2;

         if(valid) begin
          cmt_inst <= cmt_inst_delay3;
        end
        else begin
          cmt_inst <= cmt_inst;
        end

        cmt_valid <= valid;

       // trap_code <= 8'b0;
        cycleCnt <= cycleCnt + 1'b1;
        instrCnt <= valid ? instrCnt + 64'b1 : instrCnt;
      end
    end

/*    //skip nop
    reg [3:0] skip;
    always @(posedge clock) begin
      if(reset) skip <= 4'b1;
      else begin
        skip[3] <= stall[3] | (skip[2] & ~mem_okay);
        skip[2] <= flush | stall[2] | skip[1];
        skip[1] <= flush | stall[1] | skip[0];
        skip[0] <= flush | stall[0] | ~inst_valid;
      end
    end*/
    reg [3:0] inst_okay;
    always @(posedge clock) begin
        if(reset)      inst_okay      <= 0;
        else if(flush) begin
            inst_okay[3]   <= inst_okay[2];
            inst_okay[2:0] <= 0;
        end
        else begin
            if(~stall[3]) inst_okay[3] <= inst_okay[2];
            if(~stall[2]) inst_okay[2] <= inst_okay[1];
            if(~stall[1]) inst_okay[1] <= bubble ? 0 : inst_okay[0];
            if(~stall[0]) inst_okay[0] <= inst_valid;
        end
    end

    reg clint_skip_delay1;
    reg clint_skip_delay2;
    reg clint_skip_delay3;
    always @(posedge clock) begin
        clint_skip_delay1 <= clint_skip;
        clint_skip_delay2 <= clint_skip_delay1;
        clint_skip_delay3 <= clint_skip_delay2;
    end

    DifftestInstrCommit DifftestInstrCommit(
      .clock              (clock),
      .coreid             (0),
      .index              (0),
      .valid              (cmt_valid),
      .pc                 (cmt_pc),
      .instr              (cmt_inst),
      .skip               (clint_skip_delay3 | cmt_inst[6:0] == 7'h7b | (cmt_inst[6:0] == 7'h73 & cmt_inst[31:20] ==12'hB00) ),
      .isRVC              (0),
      .scFailed           (0),
      .wen                (cmt_wen),
      .wdest              (cmt_wdest),
      .wdata              (cmt_wdata),
      .special            (0)
    );

    DifftestArchIntRegState DifftestArchIntRegState (
      .clock              (clock),
      .coreid             (0),
      .gpr_0              (regs_diff[0]),
      .gpr_1              (regs_diff[1]),
      .gpr_2              (regs_diff[2]),
      .gpr_3              (regs_diff[3]),
      .gpr_4              (regs_diff[4]),
      .gpr_5              (regs_diff[5]),
      .gpr_6              (regs_diff[6]),
      .gpr_7              (regs_diff[7]),
      .gpr_8              (regs_diff[8]),
      .gpr_9              (regs_diff[9]),
      .gpr_10             (regs_diff[10]),
      .gpr_11             (regs_diff[11]),
      .gpr_12             (regs_diff[12]),
      .gpr_13             (regs_diff[13]),
      .gpr_14             (regs_diff[14]),
      .gpr_15             (regs_diff[15]),
      .gpr_16             (regs_diff[16]),
      .gpr_17             (regs_diff[17]),
      .gpr_18             (regs_diff[18]),
      .gpr_19             (regs_diff[19]),
      .gpr_20             (regs_diff[20]),
      .gpr_21             (regs_diff[21]),
      .gpr_22             (regs_diff[22]),
      .gpr_23             (regs_diff[23]),
      .gpr_24             (regs_diff[24]),
      .gpr_25             (regs_diff[25]),
      .gpr_26             (regs_diff[26]),
      .gpr_27             (regs_diff[27]),
      .gpr_28             (regs_diff[28]),
      .gpr_29             (regs_diff[29]),
      .gpr_30             (regs_diff[30]),
      .gpr_31             (regs_diff[31])
    );

    DifftestTrapEvent DifftestTrapEvent(
      .clock              (clock),
      .coreid             (0),
      .valid              (trap),
      .code               (|regs_diff[10]),
      .pc                 (cmt_pc),
      .cycleCnt           (cycleCnt),
      .instrCnt           (instrCnt)
    );

    DifftestCSRState DifftestCSRState(
      .clock              (clock),
      .coreid             (0),
      .priviledgeMode     (3),
      .mstatus            (mstatus),
      .sstatus            (mstatus & 64'h80000003_000DE122),
      .mepc               (mepc),
      .sepc               (0),
      .mtval              (0),
      .stval              (0),
      .mtvec              (mtvec),
      .stvec              (0),
      .mcause             (mcause),
      .scause             (0),
      .satp               (0),
      .mip                (0),
      .mie                (mie),
      .mscratch           (mscratch),
      .sscratch           (0),
      .mideleg            (0),
      .medeleg            (0)
    );

    reg timer_trap_delay1;
    always @(posedge clock) begin
        timer_trap_delay1 <= timer_trap;
    end

    DifftestArchEvent DifftestArchEvent(
      .clock(clock),
      .coreid(0),
      .intrNO(timer_trap_delay1 ? 64'h80000000_00000007 : 0),
      .cause(0),
      .exceptionPC(mepc),
      .exceptionInst()
    );

    //don't care
    DifftestArchFpRegState DifftestArchFpRegState(
      .clock              (clock),
      .coreid             (0),
      .fpr_0              (0),
      .fpr_1              (0),
      .fpr_2              (0),
      .fpr_3              (0),
      .fpr_4              (0),
      .fpr_5              (0),
      .fpr_6              (0),
      .fpr_7              (0),
      .fpr_8              (0),
      .fpr_9              (0),
      .fpr_10             (0),
      .fpr_11             (0),
      .fpr_12             (0),
      .fpr_13             (0),
      .fpr_14             (0),
      .fpr_15             (0),
      .fpr_16             (0),
      .fpr_17             (0),
      .fpr_18             (0),
      .fpr_19             (0),
      .fpr_20             (0),
      .fpr_21             (0),
      .fpr_22             (0),
      .fpr_23             (0),
      .fpr_24             (0),
      .fpr_25             (0),
      .fpr_26             (0),
      .fpr_27             (0),
      .fpr_28             (0),
      .fpr_29             (0),
      .fpr_30             (0),
      .fpr_31             (0)
    );

    //axi difftest
    wire aw_ready;
    wire aw_valid;
    wire [`AXI_ADDR_WIDTH-1:0] aw_addr;
    wire [2:0] aw_prot;
    wire [`AXI_ID_WIDTH+1:0] aw_id;
    wire [`AXI_USER_WIDTH-1:0] aw_user;
    wire [7:0] aw_len;
    wire [2:0] aw_size;
    wire [1:0] aw_burst;
    wire aw_lock;
    wire [3:0] aw_cache;
    wire [3:0] aw_qos;
    wire [3:0] aw_region;

    wire w_ready;
    wire w_valid;
    wire [`AXI_DATA_WIDTH-1:0] w_data;
    wire [`AXI_DATA_WIDTH/8-1:0] w_strb;
    wire w_last;
    wire [`AXI_USER_WIDTH-1:0] w_user;

    wire b_ready;
    wire b_valid;
    wire [1:0] b_resp;
    wire [`AXI_ID_WIDTH+1:0] b_id;
    wire [`AXI_USER_WIDTH-1:0] b_user;

    wire ar_ready;
    wire ar_valid;
    wire [`AXI_ADDR_WIDTH-1:0] ar_addr;
    wire [2:0] ar_prot;
    wire [`AXI_ID_WIDTH+1:0] ar_id;
    wire [`AXI_USER_WIDTH-1:0] ar_user;
    wire [7:0] ar_len;
    wire [2:0] ar_size;
    wire [1:0] ar_burst;
    wire ar_lock;
    wire [3:0] ar_cache;
    wire [3:0] ar_qos;
    wire [3:0] ar_region;

    wire r_ready;
    wire r_valid;
    wire [1:0] r_resp;
    wire [`AXI_DATA_WIDTH-1:0] r_data;
    wire r_last;
    wire [`AXI_ID_WIDTH+1:0] r_id;
    wire [`AXI_USER_WIDTH-1:0] r_user;

    assign ar_ready                                 = `AXI_TOP_INTERFACE(ar_ready);
    assign `AXI_TOP_INTERFACE(ar_valid)             = ar_valid;
    assign `AXI_TOP_INTERFACE(ar_bits_addr)         = ar_addr;
    assign `AXI_TOP_INTERFACE(ar_bits_prot)         = ar_prot;
    assign `AXI_TOP_INTERFACE(ar_bits_id)           = ar_id;
    assign `AXI_TOP_INTERFACE(ar_bits_user)         = ar_user;
    assign `AXI_TOP_INTERFACE(ar_bits_len)          = ar_len;
    assign `AXI_TOP_INTERFACE(ar_bits_size)         = ar_size;
    assign `AXI_TOP_INTERFACE(ar_bits_burst)        = ar_burst;
    assign `AXI_TOP_INTERFACE(ar_bits_lock)         = ar_lock;
    assign `AXI_TOP_INTERFACE(ar_bits_cache)        = ar_cache;
    assign `AXI_TOP_INTERFACE(ar_bits_qos)          = ar_qos;

    assign `AXI_TOP_INTERFACE(r_ready)              = r_ready;
    assign r_valid                                  = `AXI_TOP_INTERFACE(r_valid);
    assign r_resp                                   = `AXI_TOP_INTERFACE(r_bits_resp);
    assign r_data                                   = `AXI_TOP_INTERFACE(r_bits_data)[0];
    assign r_last                                   = `AXI_TOP_INTERFACE(r_bits_last);
    assign r_id                                     = `AXI_TOP_INTERFACE(r_bits_id);
    assign r_user                                   = `AXI_TOP_INTERFACE(r_bits_user);

    assign aw_ready                                 = `AXI_TOP_INTERFACE(aw_ready);
    assign `AXI_TOP_INTERFACE(aw_valid)             = aw_valid;
    assign `AXI_TOP_INTERFACE(aw_bits_addr)         = aw_addr;
    assign `AXI_TOP_INTERFACE(aw_bits_prot)         = aw_prot;
    assign `AXI_TOP_INTERFACE(aw_bits_id)           = aw_id;
    assign `AXI_TOP_INTERFACE(aw_bits_user)         = aw_user;
    assign `AXI_TOP_INTERFACE(aw_bits_len)          = aw_len;
    assign `AXI_TOP_INTERFACE(aw_bits_size)         = aw_size;
    assign `AXI_TOP_INTERFACE(aw_bits_burst)        = aw_burst;
    assign `AXI_TOP_INTERFACE(aw_bits_lock)         = aw_lock;
    assign `AXI_TOP_INTERFACE(aw_bits_cache)        = aw_cache;
    assign `AXI_TOP_INTERFACE(aw_bits_qos)          = aw_qos;

    assign w_ready                                  = `AXI_TOP_INTERFACE(w_ready);
    assign `AXI_TOP_INTERFACE(w_valid)              = w_valid;
    assign `AXI_TOP_INTERFACE(w_bits_data)[0]       = w_data;
    assign `AXI_TOP_INTERFACE(w_bits_strb)          = w_strb;
    assign `AXI_TOP_INTERFACE(w_bits_last)          = w_last;

    assign `AXI_TOP_INTERFACE(b_ready)              = b_ready;
    assign b_valid                                  = `AXI_TOP_INTERFACE(b_valid);
    assign b_resp                                   = `AXI_TOP_INTERFACE(b_bits_resp);
    assign b_id                                     = `AXI_TOP_INTERFACE(b_bits_id);
    assign b_user                                   = `AXI_TOP_INTERFACE(b_bits_user);

endmodule
